﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using Google.YouTube;
using Google.GData.Extensions.MediaRss;
using System.Net;
using System.IO;

namespace YoutubeGetter
{
    public partial class VideoControl : UserControl
    {
        public Video video { get; private set; }
		private Image thumbNailImage;
		private BackgroundWorker worker = new BackgroundWorker();
        private BackgroundWorker downloader = new BackgroundWorker();
		private String errorMessage;

        public VideoControl()
            : this(null)
        {
        }
        public VideoControl(Video _video)
        {
            InitializeComponent();
            if (_video != null)
            {
				this.AutoSize = true;
				this.AutoSizeMode = AutoSizeMode.GrowOnly;
                this.video = _video;
                grpInfo.Text = video.Title;
                txtDescription.Text = video.Description;
                String thumbNailLocation = video.Thumbnails[0].Url;
				worker.DoWork += new DoWorkEventHandler(worker_DoWork);
				worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
				worker.RunWorkerAsync(thumbNailLocation);
            }
        }

		private void worker_DoWork(object sender, DoWorkEventArgs e)
		{
			thumbNailImage = GetImageFromUrl((String)e.Argument);
		}

		private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
		{
			pctThumbNail.Image = thumbNailImage;
		}

        private void pctThumbNail_Click(object sender, EventArgs e)
        {
            chkSelect.Checked = !chkSelect.Checked;
        }

        private void downloader_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            if (e.ProgressPercentage > 0)
            {
                pgDownload.Value = e.ProgressPercentage;
                lblDownloadStatus.Text = "downloading...";
            }
        }

        private void downloader_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
			if (String.IsNullOrEmpty(errorMessage))
			{
				lblDownloadStatus.Text = "\"" + this.GenerateFileName() + "\" downloaded";
                pgDownload.ForeColor = Color.Green;
				pgDownload.Value = 100;
				this.Enabled = false;
			}
			else
			{
				lblDownloadStatus.Text = errorMessage;
                pgDownload.ForeColor = Color.Red;
                this.Enabled = true;
			}
        }

        private void downloader_DoWork(object sender, DoWorkEventArgs e)
        {
            System.Threading.Thread.Sleep(2); // allow other threads to start, so we have a reliable number of simultaneous downloads
			while (YoutubeTools.currentDownloads >= YoutubeTools.maxDownloads)
			{
				System.Threading.Thread.Sleep(5);
				// wait until other simultaneous downloads are finished. Let's not annoy youtube by dlding 100 videos at once :)
			}
			String downloadLocation;
            YoutubeTools.currentDownloads++;
			if (!String.IsNullOrEmpty((String)e.Argument))
			{
				downloadLocation = YoutubeTools.downloadLocation + "\\" + (String)e.Argument;
			}
			else
			{
				downloadLocation = YoutubeTools.downloadLocation;
			}

            // This code is really ugly. We don't know which formats the video is offered in.
            // All we can do is try the best quality, catch any 404 errors we get back, then try a lower quality
            // People should be bludgeoned for nesting try..catch blocks like this, but forgive me, I was desperate
            VideoQuality quality = VideoQuality.MP4_1280x720;
            String downloadUrl = YoutubeTools.GetUrlFromVideoID(video.VideoId, quality);
            if (!String.IsNullOrEmpty(downloadUrl))
            {
                WebRequest request = WebRequest.Create(downloadUrl);
                WebResponse response = null;
                try
                {
                    response = request.GetResponse();
                }
                catch (WebException)
                {
                    try
                    {
                        quality = VideoQuality.FLV_854x480;
                        request = WebRequest.Create(YoutubeTools.GetUrlFromVideoID(video.VideoId, quality));
                        response = request.GetResponse();
                    }
                    catch (WebException)
                    {
                        try
                        {
                            quality = VideoQuality.FLV_640x360;
                            request = WebRequest.Create(YoutubeTools.GetUrlFromVideoID(video.VideoId, quality));
                            response = request.GetResponse();
                        }
                        catch (WebException)
                        {
                            quality = VideoQuality.MP4_480x360;
                            request = WebRequest.Create(YoutubeTools.GetUrlFromVideoID(video.VideoId, quality));
                            response = request.GetResponse();
                        }
                    }
                }
                Stream videoStream = response.GetResponseStream();
                if (!Directory.Exists(downloadLocation))
                {
                    Directory.CreateDirectory(downloadLocation);
                }
                String fullPath = String.Format(downloadLocation + "\\" + GenerateFileName(quality));
                if (!File.Exists(fullPath))
                {
                    Stream fileStream = File.Create(fullPath);
                    byte[] buffer = new byte[1024];
                    Int32 bytesRead;
                    Int32 totalBytesDownloaded = 0;
                    try
                    {
                        do
                        {
                            bytesRead = videoStream.Read(buffer, 0, buffer.Length);
                            fileStream.Write(buffer, 0, bytesRead);
                            totalBytesDownloaded += bytesRead;
                            downloader.ReportProgress((Int32)((float)totalBytesDownloaded / (float)response.ContentLength * 100));
                        } while (bytesRead > 0);
                        videoStream.Close();
                    }
                    catch (Exception ex)
                    {
                        errorMessage = "Error: " + ex.Message;
                    }
                    finally
                    {
                        YoutubeTools.currentDownloads--;
                        fileStream.Close();
                    }
                }
                else
                {
                    errorMessage = "File already exists. Skipped.";
                    YoutubeTools.currentDownloads--;
                }
            }
            else
            {
                YoutubeTools.currentDownloads--;
                errorMessage = "Error: Could not locate file on the youtube servers";
            }
        }

        private Image GetImageFromUrl(String url)
        {
            WebRequest request = WebRequest.Create(url);
            WebResponse response = request.GetResponse();
            Image webImage = Image.FromStream(response.GetResponseStream());
            return webImage;
        }

        public String GenerateFileName(VideoQuality quality = VideoQuality.Undefined)
        {
            String extension = "." + quality.ToString().Substring(0, 3).ToLower();
            if (extension.Contains("und"))
            {
                extension = "";
            }
            String filename = video.Title;
            String[] charsToReplace = new String[] { "\'", "\"", "?", "!", "\\", "/", ":", "*" };
            foreach (String character in charsToReplace)
            {
                filename = filename.Replace(character, "");
            }
            return filename + extension;
        }
		public void DownloadVideo()
		{
			DownloadVideo(null);
		}
        public void DownloadVideo(String activeDirectory)
        {
            if (!downloader.IsBusy)
            {
                pgDownload.ForeColor = Color.LightGreen;
                downloader.DoWork += new DoWorkEventHandler(downloader_DoWork);
                downloader.RunWorkerCompleted += new RunWorkerCompletedEventHandler(downloader_RunWorkerCompleted);
                downloader.ProgressChanged += new ProgressChangedEventHandler(downloader_ProgressChanged);
                downloader.WorkerReportsProgress = true;
                downloader.WorkerSupportsCancellation = true;
                chkSelect.Enabled = false;
                downloader.RunWorkerAsync(activeDirectory);
                pnlDownload.Visible = true;
            }
        }

        public Boolean isSelected
        {
            get
            {
                return chkSelect.Checked;
            }
			set
			{
				chkSelect.Checked = value;
			}
        }
    }
}